#pragma once

#include <iostream>
#include <string>
#include <memory>
#include "Socket.hpp"
#include "Log.hpp"
#include "Epoller.hpp"

const static int gbacklog = 8;
const static int defaultmax = 64;

class EpollServer
{
public:
    EpollServer(uint16_t port)
        : _port(port), _listen_socket(new TcpSocket), _isrunning(false), _epoller(new Epoller)
    {
    }

    void HandlerEvents(int n)
    {
        for(int i = 0 ; i < n ; i++)
        {
            // 是否就绪
            if(_events[i].events & EPOLLIN)
            {
                // 普通套接字 or Listen 套接字
                int fd = _events[i].data.fd;
                // Listen 套接字
                if(fd == _listen_socket->GetSockFd())
                {
                    uint16_t clientport;
                    string clientip;
                    int sockfd = _listen_socket->AcceptConnection(&clientip,&clientport);

                    if(sockfd < 0)
                    {
                        lg.LogMessage(Error,"accept new link fail\n");
                        continue;
                    }
                    else
                    {
                        lg.LogMessage(Info,"accept new link success , client info# %s : %d\n",clientip.c_str(),clientport);
                    }

                    _epoller->AddEvent(sockfd,EPOLLIN);
                }
                else
                {
                    // 普通套接字
                    char buffer[1024];
                    int m = recv(fd,buffer,sizeof(buffer)-1,0);

                    if(m > 0)
                    {
                        buffer[m] = 0;
                        lg.LogMessage(Info,"client say# %s",buffer);
                    }
                    else
                    {
                        if(m == 0)
                        {
                            lg.LogMessage(Info,"client close link\n");
                        }
                        else
                        {
                            lg.LogMessage(Warning,"recv error\n");
                        }

                        // 进行删除操作的时候，需要确保文件描述符有效，所以不能提前关闭文件描述符
                        _epoller->DelEvents(fd);
                        close(fd);
                    }
                }
            }
        }
    }

    void InitServer()
    {
        // 创建Listen套接字
        _listen_socket->BuildListenSocketMethod(_port, gbacklog);

        int sockfd = _listen_socket->GetSockFd();
        if (sockfd == -1)
        {
            lg.LogMessage(Error, "create listen socket fail\n");
            exit(-2);
        }
        else
        {
            lg.LogMessage(Info, "create listen socket success , fd : %d\n", sockfd);
        }

        // 初始化Epoller
        _epoller->InitEpoller();

        // 添加Listen套接字的读事件关心
        _epoller->AddEvent(sockfd, EPOLLIN);

        sleep(10);
    }

    void Loop()
    {
        _isrunning = true;

        while (_isrunning)
        {
            int timeout = -1;

            int n = _epoller->Wait(_events, defaultmax, timeout);

            switch (n)
            {
            case -1:
            {
                lg.LogMessage(Fatal,"epoll wait fail\n");
                break;
            }
            case 0:
            {
                lg.LogMessage(Info,"timeout\n");
                break;
            }
            default:
            {
                lg.LogMessage(Info,"event happend\n");
                HandlerEvents(n);
                break;
            }
            }
        }
    }

    ~EpollServer() {}

private:
    uint16_t _port;
    unique_ptr<Socket> _listen_socket;
    bool _isrunning;

    unique_ptr<Epoller> _epoller;
    struct epoll_event _events[defaultmax];
};